#include "iConfig.hpp"
#include <glf/task/task.h>
#include <glf/debugger/profiler.h>
namespace glf {

Task::Task(bool manage)
	: m_DoneEvent(deferEvent)
	, m_DoneState(NONE)
	, m_IsManaged(manage)
	, m_Runnable(0)
{
}
	
Task::~Task()
{
	if(m_Runnable)
	{
		EphemeralAllocator< task_detail::Runnable > allocator;
		((task_detail::Runnable*)m_Runnable)->~Runnable();
		allocator.deallocate(m_Runnable);
	}
}

int Task::Wait(unsigned int time)
{
	// Currently a waited task must not be automatically managed (deleted by the task manager)
	// since the task could be destroyed before the call to wait. In order to support this
	// the new feature (managed and wait), a new method along the line of PushAndWait must
	// be implemented.
	GLF_ASSERT(!IsManaged());

	// Try to acquire the "wait"
	int oldState = AtomicCompareAndSwap(m_DoneState, (unsigned long)WAIT_IN_PROGRESS, (unsigned long)NONE);

	switch(oldState)
	{
		// The task is in progress, we'll setup a system event
		case NONE:
		{
			m_DoneEvent.Init();
			StoreWithRelease(m_DoneState, (unsigned long)WAIT);
			return m_DoneEvent.Wait(time);
		}
		// The DoneEvent has already been initialized we just wait
		case WAIT:
		{
			return m_DoneEvent.Wait(time);
		}
		//break; - unreachable

		// The call to task->done() is currently in progress; spin 'till it's done.
		case DONE_IN_PROGRESS:
		{
			for(int i = 0; m_DoneState != DONE; ++i) 
			{
				// spin a little, then sleep
				if(i > 64)
				{
					Thread::Sleep(0);
				}
			}
		}
		break;

		// The task is already done, quit.
		case DONE:
		break;
	}

	return 1; // success
}

void Task::Done()
{
	// Try to acquire the "done"
	int oldState = AtomicCompareAndSwap(m_DoneState, (unsigned long)DONE_IN_PROGRESS, (unsigned long)NONE);
	
	switch(oldState)
	{
		// The call to task->wait() is currently in progress; spin 'till it's done.
		case WAIT_IN_PROGRESS:
		{
			for(int i = 0; m_DoneState != WAIT; ++i) 
			{
				// spin a little, then sleep
				if(i > 64)
				{
					Thread::Sleep(0);
				}
			}
		}
		// Fallthru

		// Someone is currently waiting for the task
		case WAIT:

		// Fallthru

		// We can complete the task
		case NONE:
		{
			// Wakeup waiting threads if needs be
			if(m_DoneState == WAIT)
			{
				StoreWithRelease(m_DoneState, DONE);
				m_DoneEvent.Signal();
			}
			else
			{
				StoreWithRelease(m_DoneState, DONE);
			}
		} 
		break;
	};
}

void Task::MyRun()
{
	Run();
	Done();
}

void Task::Run()
{
#if GLF_ENABLE_DEBUGGER
	debugger::ScopeEvent dbgEvent("[glf] Task", NULL, debugger::sThreadEventType);
#endif
	task_detail::Group* oldGroup = task_detail::SetGroup(m_TaskGroup);
	GLF_ASSERT(m_Runnable);
	m_Runnable->Run();
	task_detail::SetGroup(oldGroup);
	task_detail::DropGroup(m_TaskGroup);
	m_TaskGroup = NULL;
}

void* Task::operator new (size_t size)
{
	EphemeralAllocator<char> allocator;
	return allocator.allocate(size);
}

void Task::operator delete(void* ptr)
{
	EphemeralAllocator<char> allocator;
	allocator.deallocate((char*)ptr);
}

}; // end namespace glf
